<?php

/**
 * 缓存库存类
 * @copyright Copyright&copy; 2015, 广州六象元一网络科技有限公司
 * @author 陈泽富 $
 * @version  $Id: classStock.php, v 1.0, 2015-04-14 陈泽富 $
 * 基本操作：
 * $mem                memcached实例化
 * selectStock         查询库存并保存到内存中
 * outStock            减少内存中的库存
 * addStock            增加内存中的库存
 * outStockDb          减少数据库库存
 * addStockDb          增加数据库库存
 * changeStock         修改库存（数据库与内存同时修改）
 * selectStockList     查询库存并保存到内存中(批量)
 * outStockList        减少内存中的库存(批量)
 * addStockList        增加内存中的库存(批量)
 */

class classStock {
    public  $mem; // memcached
    public  $db; // 数据库
    public  $version  = "stock".VERSION;
    private $initial  = 1000;  // 库存初值已这个为基础 小于基值则为负数
    private $maxCount = 100; // 错误最大循环次数
    private $maxTime  = 3600;  //内存失效时间
    private $config = array('512145b6f8ea49a5.m.cnqdalicm9pub001.ocs.aliyuncs.com', 11211, '512145b6f8ea49a5', 'Eggms520');

    /**
     * 构造函数
     *
     * @param   array   配置数组 : $config = array('hostname'=>'','username'=>'','password'=>'','port'=>''...);
     */
    public function __construct($config = array()) {
        if (empty($config)) {
            $config = $this->config;
        }
        $this->connect($config);
    }

    /**
     * memcached连接
     *
     * @access  public
     * @param   array   配置数组
     * @return  boolean
     */
    public function connect($config = array()) {
        $this->mem = new memcached();
        // 以二进制进行传输，当要进行密码验证时，这个一定要为true
        $this->mem->setOption(Memcached::OPT_BINARY_PROTOCOL, true);
        // 添加服务池
        $this->mem->addServer($config[0], $config[1]);
        // 进行密码验证
        $this->mem->setSaslAuthData($config[2], $config[3]);
        // 取消自动压缩，如果要后面追加数据，不可压缩数据
        $this->mem->setOption(Memcached::OPT_COMPRESSION, false);
    }
    /**
     * 查询库存并保存到内存中
     *
     * @access  public
     * @param   int        $goodsSku   商品SKU
     * @return  int        $stock      商品库存
     */
    public function selectStock($goodsSku){
        if (empty($goodsSku)) {
            return 0;
        }
        /*查询内存是否存在该库存*/
        $skuKey   = $this->skuKey($goodsSku);
        $stockBig = $this->getCounter($skuKey);
        $maxTime  = $this->maxTime;
        if (1 || $stockBig === false) {

            $sql = "SELECT goods_number AS stock FROM egg_goods_stock WHERE goods_sku = $goodsSku AND area_id = 3";
            $stock = DB()->getOne($sql);
            if (empty($stock) && $stock != 0) {
                return 0;
            }
            $stockBig = $stock+$this->initial;

            /*将数据存入内存*/
            $count = 0;
            while ($count <= $this->maxCount) {
                if ($this->mem->set($skuKey,$stockBig,$maxTime)) {
                    break;
                }
               $count++;
            }
        }
        $stock = $stockBig - $this->initial;
        return $stock;
    }
    /**
     * 添加内存中库存
     *
     * @access  public
     * @param   int        $goodsSku     商品SKU
     * @param   int        $goodsnumber  添加数量
     * @return  Boole
     */
    public function addStock($goodsSku,$goodsNumber){
            /*添加库存*/
            $count = 0;
            $skuKey=$this->skuKey($goodsSku);
            while ($count <= $this->maxCount) {
                if ( $this->mem->increment($skuKey,$goodsNumber)) {
                    return true;
                }
                else{
                    $this->selectStock($goodsSku);
                }
                $count++;
            }
            return false;
    }
    /**
     * 添加数据库库存
     *
     * @access  public
     * @param   int        $goodsSku     商品SKU
     * @param   int        $goodsnumber  添加数量
     * @return  Boole
     */
    public function addStockDb($goodsSku,$goodsNumber){
            /*添加库存*/
            $sql = "UPDATE egg_goods_stock SET goods_number = goods_number + $goodsNumber WHERE goods_sku = '$goodsSku' AND area_id = 3 LIMIT 1";
            $query = DB()->query($sql);
            return ture;
    }

    /**
     * 减少内存中库存
     *
     * @access  public
     * @param   int        $goodsSku     商品SKU
     * @param   int        $goodsnumber  减少数量
     * @return  Boole
     */
    public function outStock($goodsSku,$goodsNumber){
            /*减少库存*/
             $count = 0;
             $skuKey=$this->skuKey($goodsSku);

            while ($count <= $this->maxCount) {
                if ( $return = $this->mem->decrement($skuKey,$goodsNumber)) {
                    break;
                }
                else{
                    $this->selectStock($goodsSku);
                }
                $count++;
            }

            /*库存不足时加回库存*/
            if ($return < $this->initial) {
               $this->addStock($goodsSku,$goodsNumber);
               return false;
            }

            return true;
    }
    /**
     * 减少数据库中库存
     *
     * @access  public
     * @param   int        $goodsSku     商品SKU
     * @param   int        $goodsnumber  减少数量
     * @return  Boole
     */
    public function outStockDb($goodsSku,$goodsNumber){
            /*减少库存*/
            $sql = "UPDATE egg_goods_stock SET goods_number = goods_number - $goodsNumber WHERE goods_sku = '$goodsSku' AND area_id = 3 LIMIT 1";
            $query = DB()->query($sql);
            return true;
    }
    /**
     * 减少闪购库存
     *
     * @access  public
     * @param   int        $goodsSku     商品SKU
     * @param   int        $goodsnumber  减少数量
     * @return  Boole
     */
    public function outStockExt_limit($value){
        if (!empty($value['processed'])){
            // 已经处理过减库存信息直接返回true
            return true;
        }
        $goodsSku = !empty($value['goods_attr_sku'])?$value['goods_attr_sku']:$value['goods_sku'];
        $goodsNumber = $value['goods_number'];
       //检查商品是否在促销中
        $time = time();
        $sql =  " SELECT limit_id FROM egg_limit WHERE goods_sku = $goodsSku AND start_time <= $time AND end_time>= $time AND area_id = 3 "; 
        $limit_id =  DB()->getOne($sql);

        if(empty($limit_id)){
             return false;
        }
        /* 处理闪购库存 */
        $sql = "UPDATE egg_limit SET buy_num = buy_num + $goodsNumber ,buy_number = buy_number + $goodsNumber WHERE limit_id = $limit_id LIMIT 1";
        $query = DB()->query($sql);
        /*查询用户是否有购买改商品记录*/
        $sql =  " SELECT id FROM egg_user_limit WHERE limit_id = $limit_id AND user_id = ".$_SESSION['user_id']." AND area_id = 3";
        $id=DB()->getOne($sql);
        if($id){
            //更新数据
            $sql = "UPDATE egg_user_limit SET buy_num = buy_num  + $goodsNumber WHERE id = $id LIMIT 1";
            $query = DB()->query($sql);

        }
        else{
            //插入数据
            $sql =  " INSERT INTO egg_user_limit (limit_id , user_id,buy_num,area_id) " .
                    " VALUES( '$limit_id', '".$_SESSION['user_id']."',$goodsNumber,3)";
            $query = DB()->query($sql);

        }
        //判断闪购库存是否售完
        $sql =  " SELECT MAX(goods_num - buy_num) AS num FROM egg_limit AS l ".
                " WHERE l.start_time <= ".time()." AND l.end_time >= ".time()." AND l.goods_sku = '".$goodsSku."' " ;
        $num = DB()->getOne($sql);
        //已售完则更新促销时间
        if($num<=0){
                $sql =  " UPDATE egg_goods SET promote_end_date = '".time()."'".
                        " WHERE goods_sku = '".$goodsSku."'";
                DB()->query($sql);
        }
    }

    /* 减少订单中礼包的库存 */
    public function outStockExt_libao($value) {
        if (!empty($value['processed'])){
            // 已经处理过减库存信息直接返回true
            return true;
        }
        return true;
    }

    /* 减少订单中预购的库存 */
    public function outStockExt_yugou($value) {
        if (!empty($value['processed'])){
            // 已经处理过减库存信息直接返回true
            return true;
        }
        /* 修改已购数量 */
        $sql = "UPDATE egg_pre_order SET buy_num = buy_num + $value[goods_number] WHERE goods_id = '$value[goods_id]' AND end_time >= '" . time() . "'";
        DB()->query($sql);
        $sku = !empty($value['goods_attr_sku'])?$value['goods_attr_sku']:$value['goods_sku'];
        return $this->outStockDb($sku,$value['goods_number']);
    }

    /* 减少订单中赠品的库存 */
    public function outStockExt_gift($value) {
        if (empty($value['processed'])&&!empty($value['parent_id'])){
            /* 修改购物车数量 */
            // $sql = "UPDATE egg_activity_stock SET cart_number = cart_number - $value[goods_number] WHERE parent_id = '$value[parent_id]' ";
            // DB()->query($sql);
            // 已经处理过减库存信息直接返回true
            return true;
        }
        return true;
    }
    
    /* 减少订单中一元包邮的库存 */
    public function outStockExt_buyone($value) {
        if (!empty($value['processed'])){
            // 已经处理过减库存信息直接返回true
            return true;
        }
        return true;
    }

    /* 增加订单中闪购的库存 */
    public function addStockExt_limit($value) {
        $sku = !empty($value['goods_attr_sku'])?$value['goods_attr_sku']:$value['goods_sku'];
        $this->addStockDb($sku,$value['goods_number']);
        $this->addStock($sku,$value['goods_number']);
    }

    /* 增加订单中礼包的库存 */
    public function addStockExt_libao($value) {
        return true;
    }

    /* 增加订单中预购的库存 */
    public function addStockExt_yugou($value) {
        /* 修改已购数量 */
        $sql = "UPDATE egg_pre_order SET buy_num = buy_num - $value[goods_number] WHERE goods_id = '$value[goods_id]' AND end_time >= '" . time() . "'";
        DB()->query($sql);
        $sku = !empty($value['goods_attr_sku'])?$value['goods_attr_sku']:$value['goods_sku'];
        $this->addStockDb($sku,$value['goods_number']);
        $this->addStock($sku,$value['goods_number']);
    }

    /* 增加订单中赠品的库存 */
    public function addStockExt_gift($value) {
        $sql = "UPDATE egg_activity_stock SET valid_number = valid_number + $value[goods_number] WHERE parent_id = '$value[parent_id]' ";
        DB()->query($sql);
        return true;
    }

    /* 增加订单中一元包邮的库存 */
    public function addStockExt_buyone($value) {
        return true;
    }

    /**
     * 修改库存
     *
     * @access  public
     * @param   int        $goodsSku     商品SKU
     * @param   int        $goodsnumber  修改数量 （负数为减库存，正数为加库存）
     * @return  Boole
     */
    public function changeStock($goodsSku,$goodsNumber){
        if ($goodsNumber == 0)
        {
            return true; // 值为0即不做、增减操作，返回true
        }
        if (empty($goodsSku) || empty($goodsNumber))
        {
            return false;
        }
        if ($goodsNumber>0) {
            $this->addStock($goodsSku,$goodsNumber);
            $this->addStockDb($goodsSku,$goodsNumber);
        }
        else{
            /*减小内存库存*/
            if (!$this->outStock($goodsSku,abs($goodsNumber))) {
              return false;
            }
            $this->outStockDb($goodsSku,abs($goodsNumber));
        }
    }

    /**
     * 查询库存并保存到内存中（批量）
     *
     * @access  public
     * @param   array        $goodsSkuList   商品SKU
     * @return  int          $stock      商品库存
     */
    public function selectStockList($goodsSkuList){
        if (empty($goodsSkuList)) {
            return 0;
        }
        $maxTime    = $this->maxTime;
        $skuKeyList = $this->skuKeyList($goodsSkuList);
        /*查询内存是否存在该库存*/
        while ($count <= $this->maxCount) {
            if ($stockList =  $this->mem->getMulti($skuKeyList)) {
                 break;
            }
            $count++;
        }
        /*判断是否获取全部库存*/
        if (count($stockList) != count($skuKeyList)) {
            $skuList = implode($skuKeyList,',');
            $sql = "SELECT goods_number AS stock ,goods_sku FROM egg_goods_stock WHERE goods_sku  IN($skuList) AND area_id = 3";
            $stock = DB()->getAll($sql);
            /*循环赋值到内存*/
            foreach ($stock as $key => $value) {
                $stockBig = $value['stock']+$this->initial;
                $count = 0;
                while ($count <= $this->maxCount) {
                    if ($this->mem->set($value['goods_sku'],$stockBig,$maxTime)) {
                        break;
                    }
                    $count++;
                }
                $sotck_list[$value['goods_sku']] = $value['stock'];
            }
            if (empty($sotck_list)) {
                return 0;
            }
        }
        else{
            foreach ($stockList as $key => $value) {
                $sotck_list[$key] = $value-$this->initial;
            }
        }
        return $sotck_list;
    }
    /**
     * 减少内存中库存（批量）
     *
     * @access  public
     * @param   array      $goodsList     商品数组 value为减少数量
     * @return  Boole
     */
    public function outStockList($goodsList){
            /*减少库存*/
            $outNum = 0;
            $addNum = 0;
            $skuKeyLists=$this->skuKeyLists($goodsList);
            foreach ($skuKeyLists as $key => $value) {
                $count = 0;
                while ($count <= $this->maxCount) {
                    if ($return = $this->mem->decrement($key,$value['goodsNumber'])) {
                        break;
                    }
                    else{
                        $this->selectStock($value['sku']);
                    }
                    $count++;
                }
                /*标记运行次数*/
                $outNum++;
                    /*库存不足时加回库存*/
                if ($return < $this->initial) {
                    foreach ($skuKeyLists as $key => $value) {
                        if($outNum > $addNum){
                            $this->addStock($value['sku'],$value['goodsNumber']);
                        }
                        $addNum++;
                    }
                    return false;
                }
            }
            return true;
    }
    /**
     * 添加内存中库存(批量)
     *
     * @access  public
     * @param   int        $skuListAdd     商品列表
     * @return  Boole
     */
    public function addStockList($skuListAdd){
            /*添加库存*/
            $skuKeyLists=$this->skuKeyLists($skuListAdd);
            foreach ($skuKeyLists as $sku => $number) {
                $count = 0;
                while ($count <= $this->maxCount) {
                    if ( $this->mem->increment($sku,$number)) {
                        $count = $this->maxCount + 1;
                    }
                    else{
                        $this->selectStock($sku);
                    }
                    $count++;
                }
            }
            return ture;
    }

    /**
     * 获取计数器的值
     * @access  public
     * @param   $name  string  计数器名称
     * @return  int  返回计数器的值
     */
    public function getCounter($name = false) {
        if (!empty($name)) {
            $count = 0;
            // 取号，将计数器加1
            while ($count < $this->maxCount){
                $int = $this->mem->get($name);
                if (!empty($int) || $int == '0') {
                    return $int;
                    break;
                } else {
                    $count ++;
                }
            }
        }
        return false;
    }
    /**
     * 获取库存键值
     * @access  public
     * @param   $sku  商品SKU
     * @return  string  库存键值
    */
    public function skuKey($sku) {
        $skuKey = $this->version . $sku;
        return $skuKey;
    }

    public function skuKeyList($sku_list){
        foreach ($sku_list as $key => $value) {
            $skuKeyList[] = $this->version . $value;
        }
        return $skuKeyList;
    }
    /*重组数组*/
    public function skuKeyLists($sku_list){
        foreach ($sku_list as $key => $value) {
            $keys = !empty($value['goods_attr_sku'])?$this->version.$value['goods_attr_sku']:$this->version.$value['goods_sku'];
            $skuKeyLists[$keys]['goodsNumber'] = $value['goods_number'];
            $skuKeyLists[$keys]['sku']         = !empty($value['goods_attr_sku'])?$value['goods_attr_sku']:$value['goods_sku'];
        }
        return $skuKeyLists;
    }
    /*更新内容时间*/
    public function updateKey($goodsSku){
        $skuKey  = $this->skuKey($goodsSku);
        $maxTime = $this->maxTime;
        $count = 0;

    do {
        /* 获取内存值 */
        while ($count < $this->maxCount){
            $stock = $this->mem->get($skuKey, null, $cas);
            if (!empty($stock) || $stock == '0' ) {
                break;
            }
            $count++;
        }
        $this->mem->cas($cas, $skuKey, $stock);
    } while ($m->getResultCode() != Memcached::RES_SUCCESS);

    }

}


 ?>